# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::Ingestion::Tasks::IngestVulnerabilityStatistics, feature_category: :vulnerability_management do
  describe '#execute' do
    let_it_be(:pipeline) { create(:ci_pipeline) }
    let_it_be(:project) { pipeline.project }
    let_it_be(:security_finding_1) { create(:security_finding, severity: :critical) }
    let_it_be(:security_finding_2) { create(:security_finding, severity: :medium) }
    let_it_be(:security_finding_3) { create(:security_finding, severity: :low) }
    let_it_be(:finding_map_1) { create(:finding_map, :new_record, security_finding: security_finding_1) }
    let_it_be(:finding_map_2) { create(:finding_map, :new_record, security_finding: security_finding_2) }
    let_it_be(:finding_map_3) { create(:finding_map, :with_finding, security_finding: security_finding_3) }

    subject(:ingest_statistics) { described_class.new(pipeline, finding_maps).execute }

    context 'when there is no statistics record for the project' do
      context 'when there are no new vulnerabilities' do
        let(:finding_maps) { [finding_map_3] }

        it 'does not create a new Vulnerabilities::Statistic record' do
          expect { ingest_statistics }.not_to change { Vulnerabilities::Statistic.where(project: project).count }
        end
      end

      context 'when there are new vulnerabilities' do
        let(:finding_maps) { [finding_map_1, finding_map_2, finding_map_3] }

        it 'creates a new Vulnerabilities::Statistic record' do
          expect { ingest_statistics }.to change { Vulnerabilities::Statistic.where(project: project).count }.by(1)
        end

        it 'sets the correct attributes for the recently created record' do
          ingest_statistics

          expect(project.vulnerability_statistic).to have_attributes(critical: 1, high: 0, unknown: 0, medium: 1, low: 0, letter_grade: 'f')
        end
      end
    end

    context 'when there is already a statistics record for the project' do
      let_it_be(:vulnerability_statistic) { create(:vulnerability_statistic, :grade_c, project: project) }

      context 'when there are no new vulnerabilities' do
        let(:finding_maps) { [finding_map_3] }

        it 'does not create a new record and does not change the existing record' do
          expect { ingest_statistics }.to not_change { vulnerability_statistic.reload.letter_grade }
                                      .and not_change { vulnerability_statistic.reload.low }
                                      .and not_change { Vulnerabilities::Statistic.count }
        end
      end

      context 'when there are new vulnerabilities' do
        let(:finding_maps) { [finding_map_1, finding_map_2, finding_map_3] }

        it 'does not create a new record and updates the existing one' do
          expect { ingest_statistics }.to change { vulnerability_statistic.reload.letter_grade }.from('c').to('f')
                                      .and change { vulnerability_statistic.reload.critical }.from(0).to(1)
                                      .and change { vulnerability_statistic.reload.medium }.from(1).to(2)
                                      .and not_change { Vulnerabilities::Statistic.count }
        end
      end
    end
  end
end
